昨日我們講解了@Transactional的屬性noRollbackFor(rollbackFor)能讓原本Unchecked Exception(Checked Exception)能達到transaction的rollback(no rollback)
今日我們將介紹@Transactional當中另一個重要的屬性Isolation
在transaction concurrent的時候若發生一個在讀取一個在修改就會產生讀取上的問題,因此需要將concurrent的transaction隔離開來。那麼transaction concurrent會產生什麼樣的問題呢
Transaction1可以讀到Transaction2未提交的數據
[1]TX01 將某條balance從100修改為1000。
[2]TX02 讀取Tx01更新後的數值1000。
[3]TX01 rollback,balance回復到100。
[4]TX02 讀取到的1000為無效值。
Transaction1重複讀取同一數據會得到不一致的結果
[1]TX01 讀取balance為1000
[2]TX02 將balance修改為900。
[3]TX01 再次讀取balance,和第一次讀取的值不同。
Transaction1讀取多筆資料,重複讀取得到新插入的資料
[1]TX01 讀取ACCOUNT資訊有10筆資料
[2]TX02 向ACCOUNT插入新的資料5筆。
[3]TX01 再次讀取ACCOUNT資訊讀到15筆資料
資料庫透過Isolation level 設定可以解決concurrent的問題
Dirty read | Unrepeatable read | Phantom reads | |
---|---|---|---|
READ UNCOMMITTED | V | V | V |
READ COMMITTED | X | V | V |
REPEATABLE READ | X | X | V |
SERIALIZABLE | X | X | X |
DB連線設定
# dbconfig.properties
jdbc.user=sa
jdbc.password=sa
# 需透h2 server mode的連線方式,connection連線數才不會受限
jdbc.jdbcUrl=jdbc:h2:tcp://localhost/~/test
jdbc.driverClass=org.h2.Driver
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 加載外部文件 -->
<context:property-placeholder location="classpath:dbconfig.properties"></context:property-placeholder>
<context:component-scan base-package="com.swj"></context:component-scan>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
</bean>
<!-- 配置JdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>
<!-- transaction management -->
<bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:annotation-driven transaction-manager="tx"></tx:annotation-driven>
</beans>
@Repository
public class AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Transactional(readOnly = true,isolation = Isolation.READ_UNCOMMITTED)
//@Transactional(readOnly = true,isolation = Isolation.READ_COMMITTED)
public Integer queryAccountBalance(String accountId) {
String sql = "SELECT BALANCE FROM ACCOUNT WHERE ACCOUNT_ID = ?";
Integer balance = jdbcTemplate.queryForObject(sql, Integer.class,accountId);
return balance;
}
}
@Service
public class AccountService {
@Autowired
private AccountDao accountDao;
public Integer queryAccount(String accountId){
return accountDao.queryAccountBalance(accountId);
}
}
@Test
public void testDay28() throws FileNotFoundException {
ApplicationContext ioc = new ClassPathXmlApplicationContext("bean28.xml");
AccountService service = ioc.getBean(AccountService.class);
Integer balance = service.queryAccount("1");
System.out.println(balance);
}
UPDATE 資料庫數據未提交
Result
若dao中的isolation level修改為READ_COMMITTED,讀到的值則為1000